home *** CD-ROM | disk | FTP | other *** search
/ Ultra Pack / UltraComputing Partner Applications.iso / SunLabs / tclTK / src / tk4.0 / tkPack.c < prev    next >
C/C++ Source or Header  |  1995-05-28  |  50KB  |  1,689 lines

  1. /* 
  2.  * tkPack.c --
  3.  *
  4.  *    This file contains code to implement the "packer"
  5.  *    geometry manager for Tk.
  6.  *
  7.  * Copyright (c) 1990-1994 The Regents of the University of California.
  8.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  9.  *
  10.  * See the file "license.terms" for information on usage and redistribution
  11.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12.  */
  13.  
  14. static char sccsid[] = "@(#) tkPack.c 1.54 95/05/28 14:02:32";
  15.  
  16. #include "tkPort.h"
  17. #include "tkInt.h"
  18.  
  19. typedef enum {TOP, BOTTOM, LEFT, RIGHT} Side;
  20.  
  21. /* For each window that the packer cares about (either because
  22.  * the window is managed by the packer or because the window
  23.  * has slaves that are managed by the packer), there is a
  24.  * structure of the following type:
  25.  */
  26.  
  27. typedef struct Packer {
  28.     Tk_Window tkwin;        /* Tk token for window.  NULL means that
  29.                  * the window has been deleted, but the
  30.                  * packet hasn't had a chance to clean up
  31.                  * yet because the structure is still in
  32.                  * use. */
  33.     struct Packer *masterPtr;    /* Master window within which this window
  34.                  * is packed (NULL means this window
  35.                  * isn't managed by the packer). */
  36.     struct Packer *nextPtr;    /* Next window packed within same
  37.                  * parent.  List is priority-ordered:
  38.                  * first on list gets packed first. */
  39.     struct Packer *slavePtr;    /* First in list of slaves packed
  40.                  * inside this window (NULL means
  41.                  * no packed slaves). */
  42.     Side side;            /* Side of parent against which
  43.                  * this window is packed. */
  44.     Tk_Anchor anchor;        /* If frame allocated for window is larger
  45.                  * than window needs, this indicates how
  46.                  * where to position window in frame. */
  47.     int padX, padY;        /* Total additional pixels to leave around the
  48.                  * window (half of this space is left on each
  49.                  * side).  This is space *outside* the window:
  50.                  * we'll allocate extra space in frame but
  51.                  * won't enlarge window). */
  52.     int iPadX, iPadY;        /* Total extra pixels to allocate inside the
  53.                  * window (half this amount will appear on
  54.                  * each side). */
  55.     int doubleBw;        /* Twice the window's last known border
  56.                  * width.  If this changes, the window
  57.                  * must be repacked within its parent. */
  58.     int *abortPtr;        /* If non-NULL, it means that there is a nested
  59.                  * call to ArrangePacking already working on
  60.                  * this window.  *abortPtr may be set to 1 to
  61.                  * abort that nested call.  This happens, for
  62.                  * example, if tkwin or any of its slaves
  63.                  * is deleted. */
  64.     int flags;            /* Miscellaneous flags;  see below
  65.                  * for definitions. */
  66. } Packer;
  67.  
  68. /*
  69.  * Flag values for Packer structures:
  70.  *
  71.  * REQUESTED_REPACK:        1 means a Tk_DoWhenIdle request
  72.  *                has already been made to repack
  73.  *                all the slaves of this window.
  74.  * FILLX:            1 means if frame allocated for window
  75.  *                is wider than window needs, expand window
  76.  *                to fill frame.  0 means don't make window
  77.  *                any larger than needed.
  78.  * FILLY:            Same as FILLX, except for height.
  79.  * EXPAND:            1 means this window's frame will absorb any
  80.  *                extra space in the parent window.
  81.  * OLD_STYLE:            1 means this window is being managed with
  82.  *                the old-style packer algorithms (before
  83.  *                Tk version 3.3).  The main difference is
  84.  *                that padding and filling are done differently.
  85.  * DONT_PROPAGATE:        1 means don't set this window's requested
  86.  *                size.  0 means if this window is a master
  87.  *                then Tk will set its requested size to fit
  88.  *                the needs of its slaves.
  89.  */
  90.  
  91. #define REQUESTED_REPACK    1
  92. #define FILLX            2
  93. #define FILLY            4
  94. #define EXPAND            8
  95. #define OLD_STYLE        16
  96. #define DONT_PROPAGATE        32
  97.  
  98. /*
  99.  * Hash table used to map from Tk_Window tokens to corresponding
  100.  * Packer structures:
  101.  */
  102.  
  103. static Tcl_HashTable packerHashTable;
  104.  
  105. /*
  106.  * Have statics in this module been initialized?
  107.  */
  108.  
  109. static int initialized = 0;
  110.  
  111. /*
  112.  * The following structure is the official type record for the
  113.  * packer:
  114.  */
  115.  
  116. static void        PackReqProc _ANSI_ARGS_((ClientData clientData,
  117.                 Tk_Window tkwin));
  118. static void        PackLostSlaveProc _ANSI_ARGS_((ClientData clientData,
  119.                 Tk_Window tkwin));
  120.  
  121. static Tk_GeomMgr packerType = {
  122.     "pack",            /* name */
  123.     PackReqProc,        /* requestProc */
  124.     PackLostSlaveProc,        /* lostSlaveProc */
  125. };
  126.  
  127. /*
  128.  * Forward declarations for procedures defined later in this file:
  129.  */
  130.  
  131. static void        ArrangePacking _ANSI_ARGS_((ClientData clientData));
  132. static int        ConfigureSlaves _ANSI_ARGS_((Tcl_Interp *interp,
  133.                 Tk_Window tkwin, int argc, char *argv[]));
  134. static Packer *        GetPacker _ANSI_ARGS_((Tk_Window tkwin));
  135. static int        PackAfter _ANSI_ARGS_((Tcl_Interp *interp,
  136.                 Packer *prevPtr, Packer *masterPtr, int argc,
  137.                 char **argv));
  138. static void        PackReqProc _ANSI_ARGS_((ClientData clientData,
  139.                 Tk_Window tkwin));
  140. static void        PackStructureProc _ANSI_ARGS_((ClientData clientData,
  141.                 XEvent *eventPtr));
  142. static void        Unlink _ANSI_ARGS_((Packer *packPtr));
  143. static int        XExpansion _ANSI_ARGS_((Packer *slavePtr,
  144.                 int cavityWidth));
  145. static int        YExpansion _ANSI_ARGS_((Packer *slavePtr,
  146.                 int cavityHeight));
  147.  
  148. /*
  149.  *--------------------------------------------------------------
  150.  *
  151.  * Tk_PackCmd --
  152.  *
  153.  *    This procedure is invoked to process the "pack" Tcl command.
  154.  *    See the user documentation for details on what it does.
  155.  *
  156.  * Results:
  157.  *    A standard Tcl result.
  158.  *
  159.  * Side effects:
  160.  *    See the user documentation.
  161.  *
  162.  *--------------------------------------------------------------
  163.  */
  164.  
  165. int
  166. Tk_PackCmd(clientData, interp, argc, argv)
  167.     ClientData clientData;    /* Main window associated with
  168.                  * interpreter. */
  169.     Tcl_Interp *interp;        /* Current interpreter. */
  170.     int argc;            /* Number of arguments. */
  171.     char **argv;        /* Argument strings. */
  172. {
  173.     Tk_Window tkwin = (Tk_Window) clientData;
  174.     size_t length;
  175.     int c;
  176.  
  177.     if ((argc >= 2) && (argv[1][0] == '.')) {
  178.     return ConfigureSlaves(interp, tkwin, argc-1, argv+1);
  179.     }
  180.     if (argc < 3) {
  181.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  182.         argv[0], " option arg ?arg ...?\"", (char *) NULL);
  183.     return TCL_ERROR;
  184.     }
  185.     c = argv[1][0];
  186.     length = strlen(argv[1]);
  187.     if ((c == 'a') && (length >= 2)
  188.         && (strncmp(argv[1], "after", length) == 0)) {
  189.     Packer *prevPtr;
  190.     Tk_Window tkwin2;
  191.  
  192.     tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
  193.     if (tkwin2 == NULL) {
  194.         return TCL_ERROR;
  195.     }
  196.     prevPtr = GetPacker(tkwin2);
  197.     if (prevPtr->masterPtr == NULL) {
  198.         Tcl_AppendResult(interp, "window \"", argv[2],
  199.             "\" isn't packed", (char *) NULL);
  200.         return TCL_ERROR;
  201.     }
  202.     return PackAfter(interp, prevPtr, prevPtr->masterPtr, argc-3, argv+3);
  203.     } else if ((c == 'a') && (length >= 2)
  204.         && (strncmp(argv[1], "append", length) == 0)) {
  205.     Packer *masterPtr;
  206.     register Packer *prevPtr;
  207.     Tk_Window tkwin2;
  208.  
  209.     tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
  210.     if (tkwin2 == NULL) {
  211.         return TCL_ERROR;
  212.     }
  213.     masterPtr = GetPacker(tkwin2);
  214.     prevPtr = masterPtr->slavePtr;
  215.     if (prevPtr != NULL) {
  216.         while (prevPtr->nextPtr != NULL) {
  217.         prevPtr = prevPtr->nextPtr;
  218.         }
  219.     }
  220.     return PackAfter(interp, prevPtr, masterPtr, argc-3, argv+3);
  221.     } else if ((c == 'b') && (strncmp(argv[1], "before", length) == 0)) {
  222.     Packer *packPtr, *masterPtr;
  223.     register Packer *prevPtr;
  224.     Tk_Window tkwin2;
  225.  
  226.     tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
  227.     if (tkwin2 == NULL) {
  228.         return TCL_ERROR;
  229.     }
  230.     packPtr = GetPacker(tkwin2);
  231.     if (packPtr->masterPtr == NULL) {
  232.         Tcl_AppendResult(interp, "window \"", argv[2],
  233.             "\" isn't packed", (char *) NULL);
  234.         return TCL_ERROR;
  235.     }
  236.     masterPtr = packPtr->masterPtr;
  237.     prevPtr = masterPtr->slavePtr;
  238.     if (prevPtr == packPtr) {
  239.         prevPtr = NULL;
  240.     } else {
  241.         for ( ; ; prevPtr = prevPtr->nextPtr) {
  242.         if (prevPtr == NULL) {
  243.             panic("\"pack before\" couldn't find predecessor");
  244.         }
  245.         if (prevPtr->nextPtr == packPtr) {
  246.             break;
  247.         }
  248.         }
  249.     }
  250.     return PackAfter(interp, prevPtr, masterPtr, argc-3, argv+3);
  251.     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
  252.     if (argv[2][0] != '.') {
  253.         Tcl_AppendResult(interp, "bad argument \"", argv[2],
  254.             "\": must be name of window", (char *) NULL);
  255.         return TCL_ERROR;
  256.     }
  257.     return ConfigureSlaves(interp, tkwin, argc-2, argv+2);
  258.     } else if ((c == 'f') && (strncmp(argv[1], "forget", length) == 0)) {
  259.     Tk_Window slave;
  260.     Packer *slavePtr;
  261.     int i;
  262.  
  263.     for (i = 2; i < argc; i++) {
  264.         slave = Tk_NameToWindow(interp, argv[i], tkwin);
  265.         if (slave == NULL) {
  266.         continue;
  267.         }
  268.         slavePtr = GetPacker(slave);
  269.         if ((slavePtr != NULL) && (slavePtr->masterPtr != NULL)) {
  270.         Tk_ManageGeometry(slave, (Tk_GeomMgr *) NULL,
  271.             (ClientData) NULL);
  272.         if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) {
  273.             Tk_UnmaintainGeometry(slavePtr->tkwin,
  274.                 slavePtr->masterPtr->tkwin);
  275.         }
  276.         Unlink(slavePtr);
  277.         Tk_UnmapWindow(slavePtr->tkwin);
  278.         }
  279.     }
  280.     } else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) {
  281.     register Packer *slavePtr;
  282.     Tk_Window slave;
  283.     char buffer[300];
  284.     static char *sideNames[] = {"top", "bottom", "left", "right"};
  285.  
  286.     if (argc != 3) {
  287.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  288.             argv[0], " info window\"", (char *) NULL);
  289.         return TCL_ERROR;
  290.     }
  291.     slave = Tk_NameToWindow(interp, argv[2], tkwin);
  292.     if (slave == NULL) {
  293.         return TCL_ERROR;
  294.     }
  295.     slavePtr = GetPacker(slave);
  296.     if (slavePtr->masterPtr == NULL) {
  297.         Tcl_AppendResult(interp, "window \"", argv[2],
  298.             "\" isn't packed", (char *) NULL);
  299.         return TCL_ERROR;
  300.     }
  301.     Tcl_AppendElement(interp, "-in");
  302.     Tcl_AppendElement(interp, Tk_PathName(slavePtr->masterPtr->tkwin));
  303.     Tcl_AppendElement(interp, "-anchor");
  304.     Tcl_AppendElement(interp, Tk_NameOfAnchor(slavePtr->anchor));
  305.     Tcl_AppendResult(interp, " -expand ",
  306.         (slavePtr->flags & EXPAND) ? "1" : "0", " -fill ",
  307.         (char *) NULL);
  308.     switch (slavePtr->flags & (FILLX|FILLY)) {
  309.         case 0:
  310.         Tcl_AppendResult(interp, "none", (char *) NULL);
  311.         break;
  312.         case FILLX:
  313.         Tcl_AppendResult(interp, "x", (char *) NULL);
  314.         break;
  315.         case FILLY:
  316.         Tcl_AppendResult(interp, "y", (char *) NULL);
  317.         break;
  318.         case FILLX|FILLY:
  319.         Tcl_AppendResult(interp, "both", (char *) NULL);
  320.         break;
  321.     }
  322.     sprintf(buffer, " -ipadx %d -ipady %d -padx %d -pady %d",
  323.         slavePtr->iPadX/2, slavePtr->iPadY/2, slavePtr->padX/2,
  324.         slavePtr->padY/2);
  325.     Tcl_AppendResult(interp, buffer, " -side ", sideNames[slavePtr->side],
  326.         (char *) NULL);
  327.     } else if ((c == 'p') && (strncmp(argv[1], "propagate", length) == 0)) {
  328.     Tk_Window master;
  329.     Packer *masterPtr;
  330.     int propagate;
  331.  
  332.     if (argc > 4) {
  333.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  334.             argv[0], " propagate window ?boolean?\"", (char *) NULL);
  335.         return TCL_ERROR;
  336.     }
  337.     master = Tk_NameToWindow(interp, argv[2], tkwin);
  338.     if (master == NULL) {
  339.         return TCL_ERROR;
  340.     }
  341.     masterPtr = GetPacker(master);
  342.     if (argc == 3) {
  343.         if (masterPtr->flags & DONT_PROPAGATE) {
  344.         interp->result = "0";
  345.         } else {
  346.         interp->result = "1";
  347.         }
  348.         return TCL_OK;
  349.     }
  350.     if (Tcl_GetBoolean(interp, argv[3], &propagate) != TCL_OK) {
  351.         return TCL_ERROR;
  352.     }
  353.     if (propagate) {
  354.         masterPtr->flags &= ~DONT_PROPAGATE;
  355.  
  356.         /*
  357.          * Repack the master to allow new geometry information to
  358.          * propagate upwards to the master's master.
  359.          */
  360.  
  361.         if (masterPtr->abortPtr != NULL) {
  362.         *masterPtr->abortPtr = 1;
  363.         }
  364.         if (!(masterPtr->flags & REQUESTED_REPACK)) {
  365.         masterPtr->flags |= REQUESTED_REPACK;
  366.         Tk_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
  367.         }
  368.     } else {
  369.         masterPtr->flags |= DONT_PROPAGATE;
  370.     }
  371.     } else if ((c == 's') && (strncmp(argv[1], "slaves", length) == 0)) {
  372.     Tk_Window master;
  373.     Packer *masterPtr, *slavePtr;
  374.  
  375.     if (argc != 3) {
  376.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  377.             argv[0], " slaves window\"", (char *) NULL);
  378.         return TCL_ERROR;
  379.     }
  380.     master = Tk_NameToWindow(interp, argv[2], tkwin);
  381.     if (master == NULL) {
  382.         return TCL_ERROR;
  383.     }
  384.     masterPtr = GetPacker(master);
  385.     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  386.         slavePtr = slavePtr->nextPtr) {
  387.         Tcl_AppendElement(interp, Tk_PathName(slavePtr->tkwin));
  388.     }
  389.     } else if ((c == 'u') && (strncmp(argv[1], "unpack", length) == 0)) {
  390.     Tk_Window tkwin2;
  391.     Packer *packPtr;
  392.  
  393.     if (argc != 3) {
  394.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  395.             argv[0], " unpack window\"", (char *) NULL);
  396.         return TCL_ERROR;
  397.     }
  398.     tkwin2 = Tk_NameToWindow(interp, argv[2], tkwin);
  399.     if (tkwin2 == NULL) {
  400.         return TCL_ERROR;
  401.     }
  402.     packPtr = GetPacker(tkwin2);
  403.     if ((packPtr != NULL) && (packPtr->masterPtr != NULL)) {
  404.         Tk_ManageGeometry(tkwin2, (Tk_GeomMgr *) NULL,
  405.             (ClientData) NULL);
  406.         if (packPtr->masterPtr->tkwin != Tk_Parent(packPtr->tkwin)) {
  407.         Tk_UnmaintainGeometry(packPtr->tkwin,
  408.             packPtr->masterPtr->tkwin);
  409.         }
  410.         Unlink(packPtr);
  411.         Tk_UnmapWindow(packPtr->tkwin);
  412.     }
  413.     } else {
  414.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  415.         "\":  must be configure, forget, info, ",
  416.         "propagate, or slaves", (char *) NULL);
  417.     return TCL_ERROR;
  418.     }
  419.     return TCL_OK;
  420. }
  421.  
  422. /*
  423.  *--------------------------------------------------------------
  424.  *
  425.  * PackReqProc --
  426.  *
  427.  *    This procedure is invoked by Tk_GeometryRequest for
  428.  *    windows managed by the packer.
  429.  *
  430.  * Results:
  431.  *    None.
  432.  *
  433.  * Side effects:
  434.  *    Arranges for tkwin, and all its managed siblings, to
  435.  *    be re-packed at the next idle point.
  436.  *
  437.  *--------------------------------------------------------------
  438.  */
  439.  
  440.     /* ARGSUSED */
  441. static void
  442. PackReqProc(clientData, tkwin)
  443.     ClientData clientData;    /* Packer's information about
  444.                  * window that got new preferred
  445.                  * geometry.  */
  446.     Tk_Window tkwin;        /* Other Tk-related information
  447.                  * about the window. */
  448. {
  449.     register Packer *packPtr = (Packer *) clientData;
  450.  
  451.     packPtr = packPtr->masterPtr;
  452.     if (!(packPtr->flags & REQUESTED_REPACK)) {
  453.     packPtr->flags |= REQUESTED_REPACK;
  454.     Tk_DoWhenIdle(ArrangePacking, (ClientData) packPtr);
  455.     }
  456. }
  457.  
  458. /*
  459.  *--------------------------------------------------------------
  460.  *
  461.  * PackLostSlaveProc --
  462.  *
  463.  *    This procedure is invoked by Tk whenever some other geometry
  464.  *    claims control over a slave that used to be managed by us.
  465.  *
  466.  * Results:
  467.  *    None.
  468.  *
  469.  * Side effects:
  470.  *    Forgets all packer-related information about the slave.
  471.  *
  472.  *--------------------------------------------------------------
  473.  */
  474.  
  475.     /* ARGSUSED */
  476. static void
  477. PackLostSlaveProc(clientData, tkwin)
  478.     ClientData clientData;    /* Packer structure for slave window that
  479.                  * was stolen away. */
  480.     Tk_Window tkwin;        /* Tk's handle for the slave window. */
  481. {
  482.     register Packer *slavePtr = (Packer *) clientData;
  483.  
  484.     if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) {
  485.     Tk_UnmaintainGeometry(slavePtr->tkwin, slavePtr->masterPtr->tkwin);
  486.     }
  487.     Unlink(slavePtr);
  488.     Tk_UnmapWindow(slavePtr->tkwin);
  489. }
  490.  
  491. /*
  492.  *--------------------------------------------------------------
  493.  *
  494.  * ArrangePacking --
  495.  *
  496.  *    This procedure is invoked (using the Tk_DoWhenIdle
  497.  *    mechanism) to re-layout a set of windows managed by
  498.  *    the packer.  It is invoked at idle time so that a
  499.  *    series of packer requests can be merged into a single
  500.  *    layout operation.
  501.  *
  502.  * Results:
  503.  *    None.
  504.  *
  505.  * Side effects:
  506.  *    The packed slaves of masterPtr may get resized or
  507.  *    moved.
  508.  *
  509.  *--------------------------------------------------------------
  510.  */
  511.  
  512. static void
  513. ArrangePacking(clientData)
  514.     ClientData clientData;    /* Structure describing parent whose slaves
  515.                  * are to be re-layed out. */
  516. {
  517.     register Packer *masterPtr = (Packer *) clientData;
  518.     register Packer *slavePtr;    
  519.     int cavityX, cavityY, cavityWidth, cavityHeight;
  520.                 /* These variables keep track of the
  521.                  * as-yet-unallocated space remaining in
  522.                  * the middle of the parent window. */
  523.     int frameX, frameY, frameWidth, frameHeight;
  524.                 /* These variables keep track of the frame
  525.                  * allocated to the current window. */
  526.     int x, y, width, height;    /* These variables are used to hold the
  527.                  * actual geometry of the current window. */
  528.     int intBWidth;        /* Width of internal border in parent window,
  529.                  * if any. */
  530.     int abort;            /* May get set to non-zero to abort this
  531.                  * repacking operation. */
  532.     int borderX, borderY;
  533.     int maxWidth, maxHeight, tmp;
  534.  
  535.     masterPtr->flags &= ~REQUESTED_REPACK;
  536.  
  537.     /*
  538.      * If the parent has no slaves anymore, then don't do anything
  539.      * at all:  just leave the parent's size as-is.
  540.      */
  541.  
  542.     if (masterPtr->slavePtr == NULL) {
  543.     return;
  544.     }
  545.  
  546.     /*
  547.      * Abort any nested call to ArrangePacking for this window, since
  548.      * we'll do everything necessary here, and set up so this call
  549.      * can be aborted if necessary.  
  550.      */
  551.  
  552.     if (masterPtr->abortPtr != NULL) {
  553.     *masterPtr->abortPtr = 1;
  554.     }
  555.     masterPtr->abortPtr = &abort;
  556.     abort = 0;
  557.     Tk_Preserve((ClientData) masterPtr);
  558.  
  559.     /*
  560.      * Pass #1: scan all the slaves to figure out the total amount
  561.      * of space needed.  Two separate width and height values are
  562.      * computed:
  563.      *
  564.      * width -        Holds the sum of the widths (plus padding) of
  565.      *            all the slaves seen so far that were packed LEFT
  566.      *            or RIGHT.
  567.      * height -        Holds the sum of the heights (plus padding) of
  568.      *            all the slaves seen so far that were packed TOP
  569.      *            or BOTTOM.
  570.      *
  571.      * maxWidth -    Gradually builds up the width needed by the master
  572.      *            to just barely satisfy all the slave's needs.  For
  573.      *            each slave, the code computes the width needed for
  574.      *            all the slaves so far and updates maxWidth if the
  575.      *            new value is greater.
  576.      * maxHeight -    Same as maxWidth, except keeps height info.
  577.      */
  578.  
  579.     intBWidth = Tk_InternalBorderWidth(masterPtr->tkwin);
  580.     width = height = maxWidth = maxHeight = 2*intBWidth;
  581.     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  582.         slavePtr = slavePtr->nextPtr) {
  583.     if ((slavePtr->side == TOP) || (slavePtr->side == BOTTOM)) {
  584.         tmp = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->doubleBw
  585.             + slavePtr->padX + slavePtr->iPadX + width;
  586.         if (tmp > maxWidth) {
  587.         maxWidth = tmp;
  588.         }
  589.         height += Tk_ReqHeight(slavePtr->tkwin) + slavePtr->doubleBw
  590.             + slavePtr->padY + slavePtr->iPadY;
  591.     } else {
  592.         tmp = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->doubleBw
  593.             + slavePtr->padY + slavePtr->iPadY + height;
  594.         if (tmp > maxHeight) {
  595.         maxHeight = tmp;
  596.         }
  597.         width += Tk_ReqWidth(slavePtr->tkwin) + slavePtr->doubleBw
  598.             + slavePtr->padX + slavePtr->iPadX;
  599.     }
  600.     }
  601.     if (width > maxWidth) {
  602.     maxWidth = width;
  603.     }
  604.     if (height > maxHeight) {
  605.     maxHeight = height;
  606.     }
  607.  
  608.     /*
  609.      * If the total amount of space needed in the parent window has
  610.      * changed, and if we're propagating geometry information, then
  611.      * notify the next geometry manager up and requeue ourselves to
  612.      * start again after the parent has had a chance to
  613.      * resize us.
  614.      */
  615.  
  616.     if (((maxWidth != Tk_ReqWidth(masterPtr->tkwin))
  617.         || (maxHeight != Tk_ReqHeight(masterPtr->tkwin)))
  618.         && !(masterPtr->flags & DONT_PROPAGATE)) {
  619.     Tk_GeometryRequest(masterPtr->tkwin, maxWidth, maxHeight);
  620.     masterPtr->flags |= REQUESTED_REPACK;
  621.     Tk_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
  622.     goto done;
  623.     }
  624.  
  625.     /*
  626.      * Pass #2: scan the slaves a second time assigning
  627.      * new sizes.  The "cavity" variables keep track of the
  628.      * unclaimed space in the cavity of the window;  this
  629.      * shrinks inward as we allocate windows around the
  630.      * edges.  The "frame" variables keep track of the space
  631.      * allocated to the current window and its frame.  The
  632.      * current window is then placed somewhere inside the
  633.      * frame, depending on anchor.
  634.      */
  635.  
  636.     cavityX = cavityY = x = y = intBWidth;
  637.     cavityWidth = Tk_Width(masterPtr->tkwin) - 2*intBWidth;
  638.     cavityHeight = Tk_Height(masterPtr->tkwin) - 2*intBWidth;
  639.     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  640.         slavePtr = slavePtr->nextPtr) {
  641.     if ((slavePtr->side == TOP) || (slavePtr->side == BOTTOM)) {
  642.         frameWidth = cavityWidth;
  643.         frameHeight = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->doubleBw
  644.             + slavePtr->padY + slavePtr->iPadY;
  645.         if (slavePtr->flags & EXPAND) {
  646.         frameHeight += YExpansion(slavePtr, cavityHeight);
  647.         }
  648.         cavityHeight -= frameHeight;
  649.         if (cavityHeight < 0) {
  650.         frameHeight += cavityHeight;
  651.         cavityHeight = 0;
  652.         }
  653.         frameX = cavityX;
  654.         if (slavePtr->side == TOP) {
  655.         frameY = cavityY;
  656.         cavityY += frameHeight;
  657.         } else {
  658.         frameY = cavityY + cavityHeight;
  659.         }
  660.     } else {
  661.         frameHeight = cavityHeight;
  662.         frameWidth = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->doubleBw
  663.             + slavePtr->padX + slavePtr->iPadX;
  664.         if (slavePtr->flags & EXPAND) {
  665.         frameWidth += XExpansion(slavePtr, cavityWidth);
  666.         }
  667.         cavityWidth -= frameWidth;
  668.         if (cavityWidth < 0) {
  669.         frameWidth += cavityWidth;
  670.         cavityWidth = 0;
  671.         }
  672.         frameY = cavityY;
  673.         if (slavePtr->side == LEFT) {
  674.         frameX = cavityX;
  675.         cavityX += frameWidth;
  676.         } else {
  677.         frameX = cavityX + cavityWidth;
  678.         }
  679.     }
  680.  
  681.     /*
  682.      * Now that we've got the size of the frame for the window,
  683.      * compute the window's actual size and location using the
  684.      * fill, padding, and frame factors.  The variables "borderX"
  685.      * and "borderY" are used to handle the differences between
  686.      * old-style packing and the new style (in old-style, iPadX
  687.      * and iPadY are always zero and padding is completely ignored
  688.      * except when computing frame size).
  689.      */
  690.  
  691.     if (slavePtr->flags & OLD_STYLE) {
  692.         borderX = borderY = 0;
  693.     } else {
  694.         borderX = slavePtr->padX;
  695.         borderY = slavePtr->padY;
  696.     }
  697.     width = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->doubleBw
  698.         + slavePtr->iPadX;
  699.     if ((slavePtr->flags & FILLX)
  700.         || (width > (frameWidth - borderX))) {
  701.         width = frameWidth - borderX;
  702.     }
  703.     height = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->doubleBw
  704.         + slavePtr->iPadY;
  705.     if ((slavePtr->flags & FILLY)
  706.         || (height > (frameHeight - borderY))) {
  707.         height = frameHeight - borderY;
  708.     }
  709.     borderX /= 2;
  710.     borderY /= 2;
  711.     switch (slavePtr->anchor) {
  712.         case TK_ANCHOR_N:
  713.         x = frameX + (frameWidth - width)/2;
  714.         y = frameY + borderY;
  715.         break;
  716.         case TK_ANCHOR_NE:
  717.         x = frameX + frameWidth - width - borderX;
  718.         y = frameY + borderY;
  719.         break;
  720.         case TK_ANCHOR_E:
  721.         x = frameX + frameWidth - width - borderX;
  722.         y = frameY + (frameHeight - height)/2;
  723.         break;
  724.         case TK_ANCHOR_SE:
  725.         x = frameX + frameWidth - width - borderX;
  726.         y = frameY + frameHeight - height - borderY;
  727.         break;
  728.         case TK_ANCHOR_S:
  729.         x = frameX + (frameWidth - width)/2;
  730.         y = frameY + frameHeight - height - borderY;
  731.         break;
  732.         case TK_ANCHOR_SW:
  733.         x = frameX + borderX;
  734.         y = frameY + frameHeight - height - borderY;
  735.         break;
  736.         case TK_ANCHOR_W:
  737.         x = frameX + borderX;
  738.         y = frameY + (frameHeight - height)/2;
  739.         break;
  740.         case TK_ANCHOR_NW:
  741.         x = frameX + borderX;
  742.         y = frameY + borderY;
  743.         break;
  744.         case TK_ANCHOR_CENTER:
  745.         x = frameX + (frameWidth - width)/2;
  746.         y = frameY + (frameHeight - height)/2;
  747.         break;
  748.         default:
  749.         panic("bad frame factor in ArrangePacking");
  750.     }
  751.     width -= slavePtr->doubleBw;
  752.     height -= slavePtr->doubleBw;
  753.  
  754.     /*
  755.      * The final step is to set the position, size, and mapped/unmapped
  756.      * state of the slave.  If the slave is a child of the master, then
  757.      * do this here.  Otherwise let Tk_MaintainGeometry do the work.
  758.      */
  759.  
  760.     if (masterPtr->tkwin == Tk_Parent(slavePtr->tkwin)) {
  761.         if ((width <= 0) || (height <= 0)) {
  762.         Tk_UnmapWindow(slavePtr->tkwin);
  763.         } else {
  764.         if ((x != Tk_X(slavePtr->tkwin))
  765.             || (y != Tk_Y(slavePtr->tkwin))
  766.             || (width != Tk_Width(slavePtr->tkwin))
  767.             || (height != Tk_Height(slavePtr->tkwin))) {
  768.             Tk_MoveResizeWindow(slavePtr->tkwin, x, y, width, height);
  769.         }
  770.         if (abort) {
  771.             goto done;
  772.         }
  773.         Tk_MapWindow(slavePtr->tkwin);
  774.         }
  775.     } else {
  776.         if ((width <= 0) || (height <= 0)) {
  777.         Tk_UnmaintainGeometry(slavePtr->tkwin, masterPtr->tkwin);
  778.         Tk_UnmapWindow(slavePtr->tkwin);
  779.         } else {
  780.         Tk_MaintainGeometry(slavePtr->tkwin, masterPtr->tkwin,
  781.             x, y, width, height);
  782.         }
  783.     }
  784.  
  785.     /*
  786.      * Changes to the window's structure could cause almost anything
  787.      * to happen, including deleting the parent or child.  If this
  788.      * happens, we'll be told to abort.
  789.      */
  790.  
  791.     if (abort) {
  792.         goto done;
  793.     }
  794.     }
  795.  
  796.     done:
  797.     masterPtr->abortPtr = NULL;
  798.     Tk_Release((ClientData) masterPtr);
  799. }
  800.  
  801. /*
  802.  *----------------------------------------------------------------------
  803.  *
  804.  * XExpansion --
  805.  *
  806.  *    Given a list of packed slaves, the first of which is packed
  807.  *    on the left or right and is expandable, compute how much to
  808.  *    expand the child.
  809.  *
  810.  * Results:
  811.  *    The return value is the number of additional pixels to give to
  812.  *    the child.
  813.  *
  814.  * Side effects:
  815.  *    None.
  816.  *
  817.  *----------------------------------------------------------------------
  818.  */
  819.  
  820. static int
  821. XExpansion(slavePtr, cavityWidth)
  822.     register Packer *slavePtr;        /* First in list of remaining
  823.                      * slaves. */
  824.     int cavityWidth;            /* Horizontal space left for all
  825.                      * remaining slaves. */
  826. {
  827.     int numExpand, minExpand, curExpand;
  828.     int childWidth;
  829.  
  830.     /*
  831.      * This procedure is tricky because windows packed top or bottom can
  832.      * be interspersed among expandable windows packed left or right.
  833.      * Scan through the list, keeping a running sum of the widths of
  834.      * all left and right windows (actually, count the cavity space not
  835.      * allocated) and a running count of all expandable left and right
  836.      * windows.  At each top or bottom window, and at the end of the
  837.      * list, compute the expansion factor that seems reasonable at that
  838.      * point.  Return the smallest factor seen at any of these points.
  839.      */
  840.  
  841.     minExpand = cavityWidth;
  842.     numExpand = 0;
  843.     for ( ; slavePtr != NULL; slavePtr = slavePtr->nextPtr) {
  844.     childWidth = Tk_ReqWidth(slavePtr->tkwin) + slavePtr->doubleBw
  845.         + slavePtr->padX + slavePtr->iPadX;
  846.     if ((slavePtr->side == TOP) || (slavePtr->side == BOTTOM)) {
  847.         curExpand = (cavityWidth - childWidth)/numExpand;
  848.         if (curExpand < minExpand) {
  849.         minExpand = curExpand;
  850.         }
  851.     } else {
  852.         cavityWidth -= childWidth;
  853.         if (slavePtr->flags & EXPAND) {
  854.         numExpand++;
  855.         }
  856.     }
  857.     }
  858.     curExpand = cavityWidth/numExpand;
  859.     if (curExpand < minExpand) {
  860.     minExpand = curExpand;
  861.     }
  862.     return (minExpand < 0) ? 0 : minExpand;
  863. }
  864.  
  865. /*
  866.  *----------------------------------------------------------------------
  867.  *
  868.  * YExpansion --
  869.  *
  870.  *    Given a list of packed slaves, the first of which is packed
  871.  *    on the top or bottom and is expandable, compute how much to
  872.  *    expand the child.
  873.  *
  874.  * Results:
  875.  *    The return value is the number of additional pixels to give to
  876.  *    the child.
  877.  *
  878.  * Side effects:
  879.  *    None.
  880.  *
  881.  *----------------------------------------------------------------------
  882.  */
  883.  
  884. static int
  885. YExpansion(slavePtr, cavityHeight)
  886.     register Packer *slavePtr;        /* First in list of remaining
  887.                      * slaves. */
  888.     int cavityHeight;            /* Vertical space left for all
  889.                      * remaining slaves. */
  890. {
  891.     int numExpand, minExpand, curExpand;
  892.     int childHeight;
  893.  
  894.     /*
  895.      * See comments for XExpansion.
  896.      */
  897.  
  898.     minExpand = cavityHeight;
  899.     numExpand = 0;
  900.     for ( ; slavePtr != NULL; slavePtr = slavePtr->nextPtr) {
  901.     childHeight = Tk_ReqHeight(slavePtr->tkwin) + slavePtr->doubleBw
  902.         + slavePtr->padY + slavePtr->iPadY;
  903.     if ((slavePtr->side == LEFT) || (slavePtr->side == RIGHT)) {
  904.         curExpand = (cavityHeight - childHeight)/numExpand;
  905.         if (curExpand < minExpand) {
  906.         minExpand = curExpand;
  907.         }
  908.     } else {
  909.         cavityHeight -= childHeight;
  910.         if (slavePtr->flags & EXPAND) {
  911.         numExpand++;
  912.         }
  913.     }
  914.     }
  915.     curExpand = cavityHeight/numExpand;
  916.     if (curExpand < minExpand) {
  917.     minExpand = curExpand;
  918.     }
  919.     return (minExpand < 0) ? 0 : minExpand;
  920. }
  921.  
  922. /*
  923.  *--------------------------------------------------------------
  924.  *
  925.  * GetPacker --
  926.  *
  927.  *    This internal procedure is used to locate a Packer
  928.  *    structure for a given window, creating one if one
  929.  *    doesn't exist already.
  930.  *
  931.  * Results:
  932.  *    The return value is a pointer to the Packer structure
  933.  *    corresponding to tkwin.
  934.  *
  935.  * Side effects:
  936.  *    A new packer structure may be created.  If so, then
  937.  *    a callback is set up to clean things up when the
  938.  *    window is deleted.
  939.  *
  940.  *--------------------------------------------------------------
  941.  */
  942.  
  943. static Packer *
  944. GetPacker(tkwin)
  945.     Tk_Window tkwin;        /* Token for window for which
  946.                  * packer structure is desired. */
  947. {
  948.     register Packer *packPtr;
  949.     Tcl_HashEntry *hPtr;
  950.     int new;
  951.  
  952.     if (!initialized) {
  953.     initialized = 1;
  954.     Tcl_InitHashTable(&packerHashTable, TCL_ONE_WORD_KEYS);
  955.     }
  956.  
  957.     /*
  958.      * See if there's already packer for this window.  If not,
  959.      * then create a new one.
  960.      */
  961.  
  962.     hPtr = Tcl_CreateHashEntry(&packerHashTable, (char *) tkwin, &new);
  963.     if (!new) {
  964.     return (Packer *) Tcl_GetHashValue(hPtr);
  965.     }
  966.     packPtr = (Packer *) ckalloc(sizeof(Packer));
  967.     packPtr->tkwin = tkwin;
  968.     packPtr->masterPtr = NULL;
  969.     packPtr->nextPtr = NULL;
  970.     packPtr->slavePtr = NULL;
  971.     packPtr->side = TOP;
  972.     packPtr->anchor = TK_ANCHOR_CENTER;
  973.     packPtr->padX = packPtr->padY = 0;
  974.     packPtr->iPadX = packPtr->iPadY = 0;
  975.     packPtr->doubleBw = 2*Tk_Changes(tkwin)->border_width;
  976.     packPtr->abortPtr = NULL;
  977.     packPtr->flags = 0;
  978.     Tcl_SetHashValue(hPtr, packPtr);
  979.     Tk_CreateEventHandler(tkwin, StructureNotifyMask,
  980.         PackStructureProc, (ClientData) packPtr);
  981.     return packPtr;
  982. }
  983.  
  984. /*
  985.  *--------------------------------------------------------------
  986.  *
  987.  * PackAfter --
  988.  *
  989.  *    This procedure does most of the real work of adding
  990.  *    one or more windows into the packing order for its parent.
  991.  *
  992.  * Results:
  993.  *    A standard Tcl return value.
  994.  *
  995.  * Side effects:
  996.  *    The geometry of the specified windows may change, both now and
  997.  *    again in the future.
  998.  *
  999.  *--------------------------------------------------------------
  1000.  */
  1001.  
  1002. static int
  1003. PackAfter(interp, prevPtr, masterPtr, argc, argv)
  1004.     Tcl_Interp *interp;        /* Interpreter for error reporting. */
  1005.     Packer *prevPtr;        /* Pack windows in argv just after this
  1006.                  * window;  NULL means pack as first
  1007.                  * child of masterPtr. */
  1008.     Packer *masterPtr;        /* Master in which to pack windows. */
  1009.     int argc;            /* Number of elements in argv. */
  1010.     char **argv;        /* Array of lists, each containing 2
  1011.                  * elements:  window name and side
  1012.                  * against which to pack. */
  1013. {
  1014.     register Packer *packPtr;
  1015.     Tk_Window tkwin, ancestor, parent;
  1016.     size_t length;
  1017.     char **options;
  1018.     int index, tmp, optionCount, c;
  1019.  
  1020.     /*
  1021.      * Iterate over all of the window specifiers, each consisting of
  1022.      * two arguments.  The first argument contains the window name and
  1023.      * the additional arguments contain options such as "top" or
  1024.      * "padx 20".
  1025.      */
  1026.  
  1027.     for ( ; argc > 0; argc -= 2, argv += 2, prevPtr = packPtr) {
  1028.     if (argc < 2) {
  1029.         Tcl_AppendResult(interp, "wrong # args: window \"",
  1030.             argv[0], "\" should be followed by options",
  1031.             (char *) NULL);
  1032.         return TCL_ERROR;
  1033.     }
  1034.  
  1035.     /*
  1036.      * Find the packer for the window to be packed, and make sure
  1037.      * that the window in which it will be packed is either its
  1038.      * or a descendant of its parent.
  1039.      */
  1040.  
  1041.     tkwin = Tk_NameToWindow(interp, argv[0], masterPtr->tkwin);
  1042.     if (tkwin == NULL) {
  1043.         return TCL_ERROR;
  1044.     }
  1045.  
  1046.     parent = Tk_Parent(tkwin);
  1047.     for (ancestor = masterPtr->tkwin; ; ancestor = Tk_Parent(ancestor)) {
  1048.         if (ancestor == parent) {
  1049.         break;
  1050.         }
  1051.         if (((Tk_FakeWin *) (ancestor))->flags & TK_TOP_LEVEL) {
  1052.         badWindow:
  1053.         Tcl_AppendResult(interp, "can't pack ", argv[0],
  1054.             " inside ", Tk_PathName(masterPtr->tkwin),
  1055.             (char *) NULL);
  1056.         return TCL_ERROR;
  1057.         }
  1058.     }
  1059.     if (((Tk_FakeWin *) (tkwin))->flags & TK_TOP_LEVEL) {
  1060.         goto badWindow;
  1061.     }
  1062.     if (tkwin == masterPtr->tkwin) {
  1063.         goto badWindow;
  1064.     }
  1065.     packPtr = GetPacker(tkwin);
  1066.  
  1067.     /*
  1068.      * Process options for this window.
  1069.      */
  1070.  
  1071.     if (Tcl_SplitList(interp, argv[1], &optionCount, &options) != TCL_OK) {
  1072.         return TCL_ERROR;
  1073.     }
  1074.     packPtr->side = TOP;
  1075.     packPtr->anchor = TK_ANCHOR_CENTER;
  1076.     packPtr->padX = packPtr->padY = 0;
  1077.     packPtr->iPadX = packPtr->iPadY = 0;
  1078.     packPtr->flags &= ~(FILLX|FILLY|EXPAND);
  1079.     packPtr->flags |= OLD_STYLE;
  1080.     for (index = 0 ; index < optionCount; index++) {
  1081.         char *curOpt = options[index];
  1082.  
  1083.         c = curOpt[0];
  1084.         length = strlen(curOpt);
  1085.  
  1086.         if ((c == 't')
  1087.             && (strncmp(curOpt, "top", length)) == 0) {
  1088.         packPtr->side = TOP;
  1089.         } else if ((c == 'b')
  1090.             && (strncmp(curOpt, "bottom", length)) == 0) {
  1091.         packPtr->side = BOTTOM;
  1092.         } else if ((c == 'l')
  1093.             && (strncmp(curOpt, "left", length)) == 0) {
  1094.         packPtr->side = LEFT;
  1095.         } else if ((c == 'r')
  1096.             && (strncmp(curOpt, "right", length)) == 0) {
  1097.         packPtr->side = RIGHT;
  1098.         } else if ((c == 'e')
  1099.             && (strncmp(curOpt, "expand", length)) == 0) {
  1100.         packPtr->flags |= EXPAND;
  1101.         } else if ((c == 'f')
  1102.             && (strcmp(curOpt, "fill")) == 0) {
  1103.         packPtr->flags |= FILLX|FILLY;
  1104.         } else if ((length == 5) && (strcmp(curOpt, "fillx")) == 0) {
  1105.         packPtr->flags |= FILLX;
  1106.         } else if ((length == 5) && (strcmp(curOpt, "filly")) == 0) {
  1107.         packPtr->flags |= FILLY;
  1108.         } else if ((c == 'p') && (strcmp(curOpt, "padx")) == 0) {
  1109.         if (optionCount < (index+2)) {
  1110.             missingPad:
  1111.             Tcl_AppendResult(interp, "wrong # args: \"", curOpt,
  1112.                 "\" option must be followed by screen distance",
  1113.                 (char *) NULL);
  1114.             goto error;
  1115.         }
  1116.         if ((Tk_GetPixels(interp, tkwin, options[index+1], &tmp)
  1117.             != TCL_OK) || (tmp < 0)) {
  1118.             badPad:
  1119.             Tcl_AppendResult(interp, "bad pad value \"",
  1120.                 options[index+1],
  1121.                 "\":  must be positive screen distance",
  1122.                 (char *) NULL);
  1123.             goto error;
  1124.         }
  1125.         packPtr->padX = tmp;
  1126.         packPtr->iPadX = 0;
  1127.         index++;
  1128.         } else if ((c == 'p') && (strcmp(curOpt, "pady")) == 0) {
  1129.         if (optionCount < (index+2)) {
  1130.             goto missingPad;
  1131.         }
  1132.         if ((Tk_GetPixels(interp, tkwin, options[index+1], &tmp)
  1133.             != TCL_OK) || (tmp < 0)) {
  1134.             goto badPad;
  1135.         }
  1136.         packPtr->padY = tmp;
  1137.         packPtr->iPadY = 0;
  1138.         index++;
  1139.         } else if ((c == 'f') && (length > 1)
  1140.             && (strncmp(curOpt, "frame", length) == 0)) {
  1141.         if (optionCount < (index+2)) {
  1142.             Tcl_AppendResult(interp, "wrong # args: \"frame\" ",
  1143.                 "option must be followed by anchor point",
  1144.                 (char *) NULL);
  1145.             goto error;
  1146.         }
  1147.         if (Tk_GetAnchor(interp, options[index+1],
  1148.             &packPtr->anchor) != TCL_OK) {
  1149.             goto error;
  1150.         }
  1151.         index++;
  1152.         } else {
  1153.         Tcl_AppendResult(interp, "bad option \"", curOpt,
  1154.             "\":  should be top, bottom, left, right, ",
  1155.             "expand, fill, fillx, filly, padx, pady, or frame",
  1156.             (char *) NULL);
  1157.         goto error;
  1158.         }
  1159.     }
  1160.  
  1161.     if (packPtr != prevPtr) {
  1162.  
  1163.         /*
  1164.          * Unpack this window if it's currently packed.
  1165.          */
  1166.  
  1167.         if (packPtr->masterPtr != NULL) {
  1168.         if ((packPtr->masterPtr != masterPtr) &&
  1169.             (packPtr->masterPtr->tkwin
  1170.             != Tk_Parent(packPtr->tkwin))) {
  1171.             Tk_UnmaintainGeometry(packPtr->tkwin,
  1172.                 packPtr->masterPtr->tkwin);
  1173.         }
  1174.         Unlink(packPtr);
  1175.         }
  1176.     
  1177.         /*
  1178.          * Add the window in the correct place in its parent's
  1179.          * packing order, then make sure that the window is
  1180.          * managed by us.
  1181.          */
  1182.  
  1183.         packPtr->masterPtr = masterPtr;
  1184.         if (prevPtr == NULL) {
  1185.         packPtr->nextPtr = masterPtr->slavePtr;
  1186.         masterPtr->slavePtr = packPtr;
  1187.         } else {
  1188.         packPtr->nextPtr = prevPtr->nextPtr;
  1189.         prevPtr->nextPtr = packPtr;
  1190.         }
  1191.         Tk_ManageGeometry(tkwin, &packerType, (ClientData) packPtr);
  1192.     }
  1193.     ckfree((char *) options);
  1194.     }
  1195.  
  1196.     /*
  1197.      * Arrange for the parent to be re-packed at the first
  1198.      * idle moment.
  1199.      */
  1200.  
  1201.     if (masterPtr->abortPtr != NULL) {
  1202.     *masterPtr->abortPtr = 1;
  1203.     }
  1204.     if (!(masterPtr->flags & REQUESTED_REPACK)) {
  1205.     masterPtr->flags |= REQUESTED_REPACK;
  1206.     Tk_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
  1207.     }
  1208.     return TCL_OK;
  1209.  
  1210.     error:
  1211.     ckfree((char *) options);
  1212.     return TCL_ERROR;
  1213. }
  1214.  
  1215. /*
  1216.  *----------------------------------------------------------------------
  1217.  *
  1218.  * Unlink --
  1219.  *
  1220.  *    Remove a packer from its parent's list of slaves.
  1221.  *
  1222.  * Results:
  1223.  *    None.
  1224.  *
  1225.  * Side effects:
  1226.  *    The parent will be scheduled for repacking.
  1227.  *
  1228.  *----------------------------------------------------------------------
  1229.  */
  1230.  
  1231. static void
  1232. Unlink(packPtr)
  1233.     register Packer *packPtr;        /* Window to unlink. */
  1234. {
  1235.     register Packer *masterPtr, *packPtr2;
  1236.  
  1237.     masterPtr = packPtr->masterPtr;
  1238.     if (masterPtr == NULL) {
  1239.     return;
  1240.     }
  1241.     if (masterPtr->slavePtr == packPtr) {
  1242.     masterPtr->slavePtr = packPtr->nextPtr;
  1243.     } else {
  1244.     for (packPtr2 = masterPtr->slavePtr; ; packPtr2 = packPtr2->nextPtr) {
  1245.         if (packPtr2 == NULL) {
  1246.         panic("Unlink couldn't find previous window");
  1247.         }
  1248.         if (packPtr2->nextPtr == packPtr) {
  1249.         packPtr2->nextPtr = packPtr->nextPtr;
  1250.         break;
  1251.         }
  1252.     }
  1253.     }
  1254.     if (!(masterPtr->flags & REQUESTED_REPACK)) {
  1255.     masterPtr->flags |= REQUESTED_REPACK;
  1256.     Tk_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
  1257.     }
  1258.     if (masterPtr->abortPtr != NULL) {
  1259.     *masterPtr->abortPtr = 1;
  1260.     }
  1261.  
  1262.     packPtr->masterPtr = NULL;
  1263. }
  1264.  
  1265. /*
  1266.  *----------------------------------------------------------------------
  1267.  *
  1268.  * DestroyPacker --
  1269.  *
  1270.  *    This procedure is invoked by Tk_EventuallyFree or Tk_Release
  1271.  *    to clean up the internal structure of a packer at a safe time
  1272.  *    (when no-one is using it anymore).
  1273.  *
  1274.  * Results:
  1275.  *    None.
  1276.  *
  1277.  * Side effects:
  1278.  *    Everything associated with the packer is freed up.
  1279.  *
  1280.  *----------------------------------------------------------------------
  1281.  */
  1282.  
  1283. static void
  1284. DestroyPacker(clientData)
  1285.     ClientData clientData;        /* Info about packed window that
  1286.                      * is now dead. */
  1287. {
  1288.     register Packer *packPtr = (Packer *) clientData;
  1289.     ckfree((char *) packPtr);
  1290. }
  1291.  
  1292. /*
  1293.  *----------------------------------------------------------------------
  1294.  *
  1295.  * PackStructureProc --
  1296.  *
  1297.  *    This procedure is invoked by the Tk event dispatcher in response
  1298.  *    to StructureNotify events.
  1299.  *
  1300.  * Results:
  1301.  *    None.
  1302.  *
  1303.  * Side effects:
  1304.  *    If a window was just deleted, clean up all its packer-related
  1305.  *    information.  If it was just resized, repack its slaves, if
  1306.  *    any.
  1307.  *
  1308.  *----------------------------------------------------------------------
  1309.  */
  1310.  
  1311. static void
  1312. PackStructureProc(clientData, eventPtr)
  1313.     ClientData clientData;        /* Our information about window
  1314.                      * referred to by eventPtr. */
  1315.     XEvent *eventPtr;            /* Describes what just happened. */
  1316. {
  1317.     register Packer *packPtr = (Packer *) clientData;
  1318.     if (eventPtr->type == ConfigureNotify) {
  1319.     if ((packPtr->slavePtr != NULL)
  1320.         && !(packPtr->flags & REQUESTED_REPACK)) {
  1321.         packPtr->flags |= REQUESTED_REPACK;
  1322.         Tk_DoWhenIdle(ArrangePacking, (ClientData) packPtr);
  1323.     }
  1324.     if (packPtr->doubleBw != 2*Tk_Changes(packPtr->tkwin)->border_width) {
  1325.         if ((packPtr->masterPtr != NULL)
  1326.             && !(packPtr->masterPtr->flags & REQUESTED_REPACK)) {
  1327.         packPtr->doubleBw = 2*Tk_Changes(packPtr->tkwin)->border_width;
  1328.         packPtr->masterPtr->flags |= REQUESTED_REPACK;
  1329.         Tk_DoWhenIdle(ArrangePacking, (ClientData) packPtr->masterPtr);
  1330.         }
  1331.     }
  1332.     } else if (eventPtr->type == DestroyNotify) {
  1333.     register Packer *slavePtr, *nextPtr;
  1334.  
  1335.     if (packPtr->masterPtr != NULL) {
  1336.         Unlink(packPtr);
  1337.     }
  1338.     for (slavePtr = packPtr->slavePtr; slavePtr != NULL;
  1339.         slavePtr = nextPtr) {
  1340.         Tk_ManageGeometry(slavePtr->tkwin, (Tk_GeomMgr *) NULL,
  1341.             (ClientData) NULL);
  1342.         Tk_UnmapWindow(slavePtr->tkwin);
  1343.         slavePtr->masterPtr = NULL;
  1344.         nextPtr = slavePtr->nextPtr;
  1345.         slavePtr->nextPtr = NULL;
  1346.     }
  1347.     Tcl_DeleteHashEntry(Tcl_FindHashEntry(&packerHashTable,
  1348.         (char *) packPtr->tkwin));
  1349.     if (packPtr->flags & REQUESTED_REPACK) {
  1350.         Tk_CancelIdleCall(ArrangePacking, (ClientData) packPtr);
  1351.     }
  1352.     packPtr->tkwin = NULL;
  1353.     Tk_EventuallyFree((ClientData) packPtr, DestroyPacker);
  1354.     }
  1355. }
  1356.  
  1357. /*
  1358.  *----------------------------------------------------------------------
  1359.  *
  1360.  * ConfigureSlaves --
  1361.  *
  1362.  *    This implements the guts of the "pack configure" command.  Given
  1363.  *    a list of slaves and configuration options, it arranges for the
  1364.  *    packer to manage the slaves and sets the specified options.
  1365.  *
  1366.  * Results:
  1367.  *    TCL_OK is returned if all went well.  Otherwise, TCL_ERROR is
  1368.  *    returned and interp->result is set to contain an error message.
  1369.  *
  1370.  * Side effects:
  1371.  *    Slave windows get taken over by the packer.
  1372.  *
  1373.  *----------------------------------------------------------------------
  1374.  */
  1375.  
  1376. static int
  1377. ConfigureSlaves(interp, tkwin, argc, argv)
  1378.     Tcl_Interp *interp;        /* Interpreter for error reporting. */
  1379.     Tk_Window tkwin;        /* Any window in application containing
  1380.                  * slaves.  Used to look up slave names. */
  1381.     int argc;            /* Number of elements in argv. */
  1382.     char *argv[];        /* Argument strings:  contains one or more
  1383.                  * window names followed by any number
  1384.                  * of "option value" pairs.  Caller must
  1385.                  * make sure that there is at least one
  1386.                  * window name. */
  1387. {
  1388.     Packer *masterPtr, *slavePtr, *prevPtr, *otherPtr;
  1389.     Tk_Window other, slave, parent, ancestor;
  1390.     int i, j, numWindows, c, tmp, positionGiven;
  1391.     size_t length;
  1392.  
  1393.     /*
  1394.      * Find out how many windows are specified.
  1395.      */
  1396.  
  1397.     for (numWindows = 0; numWindows < argc; numWindows++) {
  1398.     if (argv[numWindows][0] != '.') {
  1399.         break;
  1400.     }
  1401.     }
  1402.  
  1403.     /*
  1404.      * Iterate over all of the slave windows, parsing the configuration
  1405.      * options for each slave.  It's a bit wasteful to re-parse the
  1406.      * options for each slave, but things get too messy if we try to
  1407.      * parse the arguments just once at the beginning.  For example,
  1408.      * if a slave already is packed we want to just change a few
  1409.      * existing values without resetting everything.  If there are
  1410.      * multiple windows, the -after, -before, and -in options only
  1411.      * get processed for the first window.
  1412.      */
  1413.  
  1414.     masterPtr = NULL;
  1415.     prevPtr = NULL;
  1416.     positionGiven = 0;
  1417.     for (j = 0; j < numWindows; j++) {
  1418.     slave = Tk_NameToWindow(interp, argv[j], tkwin);
  1419.     if (slave == NULL) {
  1420.         return TCL_ERROR;
  1421.     }
  1422.     if (Tk_IsTopLevel(slave)) {
  1423.         Tcl_AppendResult(interp, "can't pack \"", argv[j],
  1424.             "\": it's a top-level window", (char *) NULL);
  1425.         return TCL_ERROR;
  1426.     }
  1427.     slavePtr = GetPacker(slave);
  1428.     slavePtr->flags &= ~OLD_STYLE;
  1429.  
  1430.     /*
  1431.      * If the slave isn't currently packed, reset all of its
  1432.      * configuration information to default values (there could
  1433.      * be old values left from a previous packing).
  1434.      */
  1435.  
  1436.     if (slavePtr->masterPtr == NULL) {
  1437.         slavePtr->side = TOP;
  1438.         slavePtr->anchor = TK_ANCHOR_CENTER;
  1439.         slavePtr->padX = slavePtr->padY = 0;
  1440.         slavePtr->iPadX = slavePtr->iPadY = 0;
  1441.         slavePtr->flags &= ~(FILLX|FILLY|EXPAND);
  1442.     }
  1443.  
  1444.     for (i = numWindows; i < argc; i+=2) {
  1445.         if ((i+2) > argc) {
  1446.         Tcl_AppendResult(interp, "extra option \"", argv[i],
  1447.             "\" (option with no value?)", (char *) NULL);
  1448.         return TCL_ERROR;
  1449.         }
  1450.         length = strlen(argv[i]);
  1451.         if (length < 2) {
  1452.         goto badOption;
  1453.         }
  1454.         c = argv[i][1];
  1455.         if ((c == 'a') && (strncmp(argv[i], "-after", length) == 0)
  1456.             && (length >= 2)) {
  1457.         if (j == 0) {
  1458.             other = Tk_NameToWindow(interp, argv[i+1], tkwin);
  1459.             if (other == NULL) {
  1460.             return TCL_ERROR;
  1461.             }
  1462.             prevPtr = GetPacker(other);
  1463.             if (prevPtr->masterPtr == NULL) {
  1464.             notPacked:
  1465.             Tcl_AppendResult(interp, "window \"", argv[i+1],
  1466.                 "\" isn't packed", (char *) NULL);
  1467.             return TCL_ERROR;
  1468.             }
  1469.             masterPtr = prevPtr->masterPtr;
  1470.             positionGiven = 1;
  1471.         }
  1472.         } else if ((c == 'a') && (strncmp(argv[i], "-anchor", length) == 0)
  1473.             && (length >= 2)) {
  1474.         if (Tk_GetAnchor(interp, argv[i+1], &slavePtr->anchor)
  1475.             != TCL_OK) {
  1476.             return TCL_ERROR;
  1477.         }
  1478.         } else if ((c == 'b')
  1479.             && (strncmp(argv[i], "-before", length) == 0)) {
  1480.         if (j == 0) {
  1481.             other = Tk_NameToWindow(interp, argv[i+1], tkwin);
  1482.             if (other == NULL) {
  1483.             return TCL_ERROR;
  1484.             }
  1485.             otherPtr = GetPacker(other);
  1486.             if (otherPtr->masterPtr == NULL) {
  1487.             goto notPacked;
  1488.             }
  1489.             masterPtr = otherPtr->masterPtr;
  1490.             prevPtr = masterPtr->slavePtr;
  1491.             if (prevPtr == otherPtr) {
  1492.             prevPtr = NULL;
  1493.             } else {
  1494.             while (prevPtr->nextPtr != otherPtr) {
  1495.                 prevPtr = prevPtr->nextPtr;
  1496.             }
  1497.             }
  1498.             positionGiven = 1;
  1499.         }
  1500.         } else if ((c == 'e')
  1501.             && (strncmp(argv[i], "-expand", length) == 0)) {
  1502.         if (Tcl_GetBoolean(interp, argv[i+1], &tmp) != TCL_OK) {
  1503.             return TCL_ERROR;
  1504.         }
  1505.         slavePtr->flags &= ~EXPAND;
  1506.         if (tmp) {
  1507.             slavePtr->flags |= EXPAND;
  1508.         }
  1509.         } else if ((c == 'f') && (strncmp(argv[i], "-fill", length) == 0)) {
  1510.         if (strcmp(argv[i+1], "none") == 0) {
  1511.             slavePtr->flags &= ~(FILLX|FILLY);
  1512.         } else if (strcmp(argv[i+1], "x") == 0) {
  1513.             slavePtr->flags = (slavePtr->flags & ~FILLY) | FILLX;
  1514.         } else if (strcmp(argv[i+1], "y") == 0) {
  1515.             slavePtr->flags = (slavePtr->flags & ~FILLX) | FILLY;
  1516.         } else if (strcmp(argv[i+1], "both") == 0) {
  1517.             slavePtr->flags |= FILLX|FILLY;
  1518.         } else {
  1519.             Tcl_AppendResult(interp, "bad fill style \"", argv[i+1],
  1520.                 "\": must be none, x, y, or both", (char *) NULL);
  1521.             return TCL_ERROR;
  1522.         }
  1523.         } else if ((c == 'i') && (strcmp(argv[i], "-in") == 0)) {
  1524.         if (j == 0) {
  1525.             other = Tk_NameToWindow(interp, argv[i+1], tkwin);
  1526.             if (other == NULL) {
  1527.             return TCL_ERROR;
  1528.             }
  1529.             masterPtr = GetPacker(other);
  1530.             prevPtr = masterPtr->slavePtr;
  1531.             if (prevPtr != NULL) {
  1532.             while (prevPtr->nextPtr != NULL) {
  1533.                 prevPtr = prevPtr->nextPtr;
  1534.             }
  1535.             }
  1536.             positionGiven = 1;
  1537.         }
  1538.         } else if ((c == 'i') && (strcmp(argv[i], "-ipadx") == 0)) {
  1539.         if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
  1540.             || (tmp < 0)) {
  1541.             badPad:
  1542.             Tcl_ResetResult(interp);
  1543.             Tcl_AppendResult(interp, "bad pad value \"", argv[i+1],
  1544.                 "\": must be positive screen distance",
  1545.                 (char *) NULL);
  1546.             return TCL_ERROR;
  1547.         }
  1548.         slavePtr->iPadX = tmp*2;
  1549.         } else if ((c == 'i') && (strcmp(argv[i], "-ipady") == 0)) {
  1550.         if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
  1551.             || (tmp< 0)) {
  1552.             goto badPad;
  1553.         }
  1554.         slavePtr->iPadY = tmp*2;
  1555.         } else if ((c == 'p') && (strcmp(argv[i], "-padx") == 0)) {
  1556.         if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
  1557.             || (tmp< 0)) {
  1558.             goto badPad;
  1559.         }
  1560.         slavePtr->padX = tmp*2;
  1561.         } else if ((c == 'p') && (strcmp(argv[i], "-pady") == 0)) {
  1562.         if ((Tk_GetPixels(interp, slave, argv[i+1], &tmp) != TCL_OK)
  1563.             || (tmp< 0)) {
  1564.             goto badPad;
  1565.         }
  1566.         slavePtr->padY = tmp*2;
  1567.         } else if ((c == 's') && (strncmp(argv[i], "-side", length) == 0)) {
  1568.         c = argv[i+1][0];
  1569.         if ((c == 't') && (strcmp(argv[i+1], "top") == 0)) {
  1570.             slavePtr->side = TOP;
  1571.         } else if ((c == 'b') && (strcmp(argv[i+1], "bottom") == 0)) {
  1572.             slavePtr->side = BOTTOM;
  1573.         } else if ((c == 'l') && (strcmp(argv[i+1], "left") == 0)) {
  1574.             slavePtr->side = LEFT;
  1575.         } else if ((c == 'r') && (strcmp(argv[i+1], "right") == 0)) {
  1576.             slavePtr->side = RIGHT;
  1577.         } else {
  1578.             Tcl_AppendResult(interp, "bad side \"", argv[i+1],
  1579.                 "\": must be top, bottom, left, or right",
  1580.                 (char *) NULL);
  1581.             return TCL_ERROR;
  1582.         }
  1583.         } else {
  1584.         badOption:
  1585.         Tcl_AppendResult(interp, "unknown or ambiguous option \"",
  1586.             argv[i], "\": must be -after, -anchor, -before, ",
  1587.             "-expand, -fill, -in, -ipadx, -ipady, -padx, ",
  1588.             "-pady, or -side", (char *) NULL);
  1589.         return TCL_ERROR;
  1590.         }
  1591.     }
  1592.  
  1593.     /*
  1594.      * If no position in a packing list was specified and the slave
  1595.      * is already packed, then leave it in its current location in
  1596.      * its current packing list.
  1597.      */
  1598.  
  1599.     if (!positionGiven && (slavePtr->masterPtr != NULL)) {
  1600.         masterPtr = slavePtr->masterPtr;
  1601.         goto scheduleLayout;
  1602.     }
  1603.  
  1604.     /*
  1605.      * If the slave is going to be put back after itself then
  1606.      * skip the whole operation, since it won't work anyway.
  1607.      */
  1608.  
  1609.     if (prevPtr == slavePtr) {
  1610.         masterPtr = slavePtr->masterPtr;
  1611.         goto scheduleLayout;
  1612.     }
  1613.     
  1614.     /*
  1615.      * If none of the "-in", "-before", or "-after" options has
  1616.      * been specified, arrange for the slave to go at the end of
  1617.      * the order for its parent.
  1618.      */
  1619.     
  1620.     if (!positionGiven) {
  1621.         masterPtr = GetPacker(Tk_Parent(slave));
  1622.         prevPtr = masterPtr->slavePtr;
  1623.         if (prevPtr != NULL) {
  1624.         while (prevPtr->nextPtr != NULL) {
  1625.             prevPtr = prevPtr->nextPtr;
  1626.         }
  1627.         }
  1628.     }
  1629.  
  1630.     /*
  1631.      * Make sure that the slave's parent is either the master or
  1632.      * an ancestor of the master.
  1633.      */
  1634.     
  1635.     parent = Tk_Parent(slave);
  1636.     for (ancestor = masterPtr->tkwin; ; ancestor = Tk_Parent(ancestor)) {
  1637.         if (ancestor == parent) {
  1638.         break;
  1639.         }
  1640.         if (Tk_IsTopLevel(ancestor)) {
  1641.         Tcl_AppendResult(interp, "can't pack ", argv[j],
  1642.             " inside ", Tk_PathName(masterPtr->tkwin),
  1643.             (char *) NULL);
  1644.         return TCL_ERROR;
  1645.         }
  1646.     }
  1647.  
  1648.     /*
  1649.      * Unpack the slave if it's currently packed, then position it
  1650.      * after prevPtr.
  1651.      */
  1652.  
  1653.     if (slavePtr->masterPtr != NULL) {
  1654.         if ((slavePtr->masterPtr != masterPtr) &&
  1655.             (slavePtr->masterPtr->tkwin
  1656.             != Tk_Parent(slavePtr->tkwin))) {
  1657.         Tk_UnmaintainGeometry(slavePtr->tkwin,
  1658.             slavePtr->masterPtr->tkwin);
  1659.         }
  1660.         Unlink(slavePtr);
  1661.     }
  1662.     slavePtr->masterPtr = masterPtr;
  1663.     if (prevPtr == NULL) {
  1664.         slavePtr->nextPtr = masterPtr->slavePtr;
  1665.         masterPtr->slavePtr = slavePtr;
  1666.     } else {
  1667.         slavePtr->nextPtr = prevPtr->nextPtr;
  1668.         prevPtr->nextPtr = slavePtr;
  1669.     }
  1670.     Tk_ManageGeometry(slave, &packerType, (ClientData) slavePtr);
  1671.     prevPtr = slavePtr;
  1672.  
  1673.     /*
  1674.      * Arrange for the parent to be re-packed at the first
  1675.      * idle moment.
  1676.      */
  1677.  
  1678.     scheduleLayout:
  1679.     if (masterPtr->abortPtr != NULL) {
  1680.         *masterPtr->abortPtr = 1;
  1681.     }
  1682.     if (!(masterPtr->flags & REQUESTED_REPACK)) {
  1683.         masterPtr->flags |= REQUESTED_REPACK;
  1684.         Tk_DoWhenIdle(ArrangePacking, (ClientData) masterPtr);
  1685.     }
  1686.     }
  1687.     return TCL_OK;
  1688. }
  1689.